package com.dmbjz.feeds.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.db.Page;
import com.dmbjz.common.constant.ApiConstant;
import com.dmbjz.common.constant.RedisKeyConstant;
import com.dmbjz.common.exception.ParameterException;
import com.dmbjz.common.model.domain.ResultInfo;
import com.dmbjz.common.model.entity.Diners;
import com.dmbjz.common.model.entity.Feeds;
import com.dmbjz.common.model.vo.FeedsVO;
import com.dmbjz.common.model.vo.ShortDinerInfo;
import com.dmbjz.common.model.vo.SingInDinerInfo;
import com.dmbjz.common.utils.AssertUtil;
import com.dmbjz.common.utils.ResultInfoUtil;
import com.dmbjz.feeds.dao.FeedsDao;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

/*发帖服务*/
@Service
@Transactional(rollbackFor = Exception.class)
public class FeedsService {


    @Value("${service.name.ms-oauth-service}")
    private String oauthServiceName;
    @Value("${service.name.ms-diner-service}")
    private String dinerServiceName;
    @Value("${service.name.ms-follow-service}")
    private String followServiceName;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private FeedsDao feedsDao;


    /*根据时间排序，每次显示20条Feed*/
    public List<FeedsVO> selectForPage(Integer page,String accessToken){

        if(page==null){
            page = 1;
        }
        SingInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);               //获取登录用户
        String key = RedisKeyConstant.following_feeds.getKey()+dinerInfo.getId();   //获取关注的FeedKey
        /*分页查询关注用户Feeds的数据*/
        long start = (page-1)* ApiConstant.PAGE_SIZE;
        long end = page * ApiConstant.PAGE_SIZE;
        Set<Integer> feedSets = redisTemplate.opsForZSet().reverseRange(key, start, end);
        /*根据ID查询Feeds,如果没有返回空*/
        if(CollectionUtil.isEmpty(feedSets)){
            return new ArrayList<FeedsVO>(1);
        }
        List<Feeds> feedsList = feedsDao.findFeedsByIds(feedSets);
        List<String> dinerIdList = new ArrayList<>(8);           //初始化关注好友的ID集合

        /*将所有Feeds数据转为FeedVo*/
        List<FeedsVO> collect = feedsList.stream().map(a -> {
            FeedsVO temoVO = new FeedsVO();
            BeanUtil.copyProperties(a, temoVO);
            dinerIdList.add(a.getFkDinerId()+"");
            return temoVO;
        }).collect(Collectors.toList());

        String idsArr = dinerIdList.stream().collect(Collectors.joining(","));
        /*将Feeds发布人的ID通过远程调用查询出具体信息*/
        ResultInfo resultInfo = restTemplate.getForObject(dinerServiceName + "findByIds?ids={id}",ResultInfo.class, idsArr);
        if( resultInfo.getCode()!=ApiConstant.SUCCESS_CODE ){
            throw new ParameterException(resultInfo.getCode(),ApiConstant.ERROR_MESSAGE);
        }
        List<LinkedHashMap> dinerMap = (List<LinkedHashMap>) resultInfo.getData();
        Map<Integer, ShortDinerInfo> dinerInfos = dinerMap.stream()
                .collect(Collectors.toMap(
                        // key
                        diner -> (Integer) diner.get("id"),
                        // value
                        diner -> BeanUtil.fillBeanWithMap(diner, new ShortDinerInfo(), true)
                ));
        // 循环 VO 集合，根据用户 ID 从 Map 中获取用户信息并设置至 VO 对象
        collect.forEach(feedsVO -> {
            feedsVO.setDinerInfo(dinerInfos.get(feedsVO.getFkDinerId()));
        });
        return collect;

    }

    /*主动删除Feed*/
    public void deleteFeeds(Integer id,String accessToken){

        AssertUtil.isTrue(id==null || id<1,"请选择需要删除的Feed");

        //通过Token获取用户信息后判断是否已被删除&是否是自己的Feed
        SingInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
        Feeds feeds = feedsDao.findById(id,dinerInfo.getId());
        AssertUtil.isTrue(feeds==null,"该Feed已被删除");
        AssertUtil.isTrue(!feeds.getFkDinerId().equals(dinerInfo.getId()),"只能删除自己的Feed");

        /*删除Feed*/
        int delete = feedsDao.delete(id);
        if(delete!=0){
            //这里将粉丝（关注）用户的推送Feed数据删除
            List<Integer> followers =  findFollowers(dinerInfo.getId());
            followers.forEach(a->{
                String key =  RedisKeyConstant.following_feeds.getKey()+a;
                redisTemplate.opsForZSet().remove(key,feeds.getId());
            } );
        }

    }


    /*主动修改Feed*/
    public void updateFeeds(Feeds feeds, String accessToken){
        /*传递用户Token和Feeds数据，修改库中存入的Feeds,删除之前给用户推送的数据，进行新的推送*/

        //对Feed内容进行校验
        AssertUtil.isNotEmpty(feeds.getContent(),"请输入内容!");
        AssertUtil.isTrue(feeds.getContent().length()>250,"内容超过限制!");
        //通过Token获取用户信息后再将Feed关联并保存
        SingInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
        Feeds getFeeds = feedsDao.findById(feeds.getId(),dinerInfo.getId());
        AssertUtil.isTrue(feeds==null,"该Feed已被删除");
        feedsDao.delete(getFeeds.getId());
        feedsDao.save(feeds);
        //这里直接推送给粉丝（关注）用户，实际是创建消息后进入MQ消息队列，由MQ订阅进行推送
        List<Integer> followers =  findFollowers(dinerInfo.getId());
        //推送Feed
        long now = System.currentTimeMillis();
        followers.forEach(a->{
            String key =  RedisKeyConstant.following_feeds.getKey()+a;
            redisTemplate.opsForZSet().add(key,feeds.getId(),now);
        } );


    }


    /*取消&添加关注时的Feed操作
    *
    * followingDinerId : 目标对象
    * accessToken : 用户登录信息
    * type : 0取关，1关注
    * */
    public void addFollowingFeed(Integer followingDinerId, String accessToken, Integer type){

        AssertUtil.isTrue(followingDinerId == null || followingDinerId < 1,"请选择关注对象");
        SingInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);                                       //获取登录用户信息
        List<Feeds> feedsList = feedsDao.findByDinerId(followingDinerId);                                   //获取目标对象的所有Feeds
        String key = RedisKeyConstant.following_feeds.getKey() + dinerInfo.getId();                         //将登录用户Feeds的Key组装
        if(type == 0){
            if(CollectionUtil.isNotEmpty(feedsList)){
                feedsList.forEach(a-> redisTemplate.opsForZSet().remove(key,a.getId()) );                   //移除所有关注期间的Feeds
            }
        }else{
            if(CollectionUtil.isNotEmpty(feedsList)){
                /*将关注对象所有的Feeds转换为Zset类型并进行添加*/
                Set<ZSetOperations.TypedTuple> feedsSet = feedsList
                        .stream()
                        .map(a->new DefaultTypedTuple<>(a.getId(),(double)a.getUpdateTime().getTime()))
                        .collect(Collectors.toSet());
                feedsList.forEach(a-> redisTemplate.opsForZSet().add(key,feedsSet) );   //添加关注对象的所有Feeds
            }
        }


    }



    /*添加贴子，将消息推送给粉丝*/
    public void create(Feeds feeds, String accessToken){

        //对Feed内容进行校验
        AssertUtil.isNotEmpty(feeds.getContent(),"请输入内容!");
        AssertUtil.isTrue(feeds.getContent().length()>250,"内容超过限制!");

        //通过Token获取用户信息后再将Feed关联并保存
        SingInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
        feeds.setFkDinerId(dinerInfo.getId());
        int save = feedsDao.save(feeds);
        AssertUtil.isTrue(save==0,"FEED添加失败!");

        //这里直接推送给粉丝（关注）用户，实际是创建消息后进入MQ消息队列，由MQ订阅进行推送
        List<Integer> followers =  findFollowers(dinerInfo.getId());
        //推送Feed
        long now = System.currentTimeMillis();
        followers.forEach(a->{
            String key =  RedisKeyConstant.following_feeds.getKey()+a;
            redisTemplate.opsForZSet().add(key,feeds.getId(),now);
        } );



    }

    /*远程调用粉丝数据查询*/
    private List<Integer> findFollowers(Integer id) {

        String url = followServiceName + "follow/followers/" + id;
        ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class);
        if(resultInfo.getCode()!=ApiConstant.SUCCESS_CODE){
            throw new ParameterException(resultInfo.getCode(),resultInfo.getMessage());
        }
        List<Integer> fanData = (List<Integer>) resultInfo.getData();
        return fanData;

    }


    /*获取登录信息*/
    private SingInDinerInfo loadSignInDinerInfo(String accessToken) {

        /*判断Token是否为空*/
        AssertUtil.mustLogin(accessToken);
        /*获取登录信息*/
        String url = oauthServiceName+"user/me?access_token={accessToken}";
        ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
        if(resultInfo.getCode()!= ApiConstant.SUCCESS_CODE){
            throw new ParameterException(resultInfo.getMessage());
        }
        SingInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap)resultInfo.getData(),new SingInDinerInfo(),false);
        return dinerInfo;

    }


}
